#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import os
import re
import copy
import datetime
import openpyxl
import pygame

from gate_api import FuturesApi, FuturesOrder, ApiClient, Configuration, Position

def playVoice(fileName):
    # 初始化pygame的mixer模块  
    pygame.mixer.init()  
  
    # 加载音频文件  
    pygame.mixer.music.load(fileName)  
  
    # 播放音频  
    pygame.mixer.music.play()  
  
    # 等待音频播放完毕  
    while pygame.mixer.music.get_busy():  
        continue

def sell_buy_comparison(apiKey, apiSecret):
    """计算合约多空比
    """

    # API管理工具
    hostUsed = "https://api.gateio.ws/api/v4"
    config = Configuration(key=apiKey, secret=apiSecret, host=hostUsed)
    futures_api = FuturesApi(ApiClient(config))

    list = futures_api.list_positions("usdt")
    # 空单保证金
    sell_margin = 0.0
    # 多单保证金
    buy_margin = 0.0
    # 空单合约价值
    sell_value = 0.0
    # 多单合约价值
    buy_vaule = 0.0
    for p in list:
    
        if isinstance(p, Position):
            # 合约交易对标识
            contract = p.contract
            # 持仓量
            size = p.size
            # 未实现盈亏
            unrealised_pnl = p.unrealised_pnl
            # 按结算币种标记价格计算的合约价值
            vaule = float(p.value)
            # 爆仓价格
            liq_price = p.liq_price
            # 开仓价格
            entry_price = p.entry_price
            # 当前风险限额下，允许的最大杠杆倍数
            leverage_max = p.leverage_max
            # 合约当前标记价格
            mark_price = float(p.mark_price)
            # 自动减仓排名
            # adl_ranking
            margins = calculateMargins(contract, size, mark_price)
            if size > 0 :
                buy_margin = buy_margin + margins
                buy_vaule = buy_vaule + vaule
                print('⭕️ 多 %s: %f' % (contract ,margins))
            else:
                sell_margin = sell_margin + margins
                sell_value = sell_value + vaule
                print('✅ 空 %s: %f' % (contract ,margins))

        else :
            print("不是 Position")
    print('✅✅✅ 保证金')
    print('buy: %f,\nsell: %f' % (buy_margin, sell_margin))
    if buy_margin > sell_margin :
        print('rate(buy/sell) : %f' % (buy_margin / sell_margin))
    else:
        print('rate(sell/buy) : %f' % (sell_margin / buy_margin))
    print('✅✅✅ 价值')

    print('buy: %f,\nsell: %f' % (buy_vaule, sell_value))
    if buy_vaule > sell_value :
        print('rate(buy/sell) : %f' % (buy_vaule / sell_value))
    else:
        print('rate(sell/buy) : %f' % (sell_value / buy_vaule))
    print('==============================================================')



def calculateWGPriceRate(contract, price, size, min_margin, min_rate):
    """计算网格单格比例（价差/价格）
    :param str contract: 交易对 (required)
    :param float price: 价格 (required)
    "param int size: 交易量/张(required)
    "param float min_margin: 最小保证金(required)
    "param float min_rate: 最小网格比例(required)
    :return: float rate(网格比例) 
    """
    
    rate = min_rate

    margins = calculateMargins(contract, size, price)
    print("%s 保证金 %f" % (contract, margins))
    rate = (margins / min_margin) * min_rate
    if rate < min_rate:
        rate = min_rate

    return rate

def calculateMargins(contract, size, price):
    """计算保证金
    :param str contract: 交易对 (required)
    :param size integer(int64): 合约头寸 (required)
    "param float price: 当前价格(required)
    :return: float margins: 保证金 
    """
    # 取绝对值
    size = abs(size)

    with open("contractList.json", 'r') as file:  
        text = file.read()  # 读取整个文件 
    list = json.loads(text) # [contractDict,contractDict...]
     # 交易对索引
    contract_indexes = {}
    for dict in list:
        contract_name = dict["name"]
        contract_indexes[contract_name] = dict # 添加到索引字典里
    # 获取目标交易对 字典
    result_dict = contract_indexes[contract]
    # 获取目标交易对 币/张
    quanto_multiplier = float(result_dict["quanto_multiplier"])
    # 获取最大杠杆倍数
    leverage_max = float(result_dict["leverage_max"])
    # 仓位价值
    position_value = size * quanto_multiplier * price
    # 保证金
    margins = 0.0

    # 其仓位起始保证金为=（仓位价值 / 杠杆） + 平仓手续费。即（200/50）+200*0.075%=4.15USDT
    margins = ((position_value) / leverage_max) + (position_value * 0.00075 * 2)

    return margins

def writeToExel(filePath, firstHList, dataDicts):
    """写入exel表格
    :param str filePath: 写入文件路径 (required)
    :param list fisrtHList: 标题列表，即 首行数据 (required)
    "param dict dataDicts: 数据 (eg.{date : (total_ear, {contract : ear})})(required)
    :return: list 
    """
    HIndexs = ["A" , "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
    # 创建一个新的工作簿
    workbook = openpyxl.Workbook()

    # 获取活动工作表（默认的工作表）
    worksheek = workbook.active
    
    # 写入数据到工作表

    worksheek[ "A1" ] = "时间"
    worksheek[ "B1" ] = "总收益"
    title_index = 2 # 水平方向索引
    # 填充标题
    for title in firstHList:
        index = HIndexs[title_index] + "1"
        worksheek[ index ] = title
        title_index = title_index + 1
    # 填充数据
    dataVIndex = 1 # 垂直方向索引
    for dataKey in dataDicts:
        
        dataVIndex = dataVIndex + 1
        dateIndex = "A" + str(dataVIndex)
        totalEarIndex = "B" + str(dataVIndex)
        worksheek[ dateIndex ] = dataKey
        data = dataDicts[ dataKey ]
        # 总收益/天
        total_ear = data[ 0 ]
        worksheek[ totalEarIndex ] = total_ear
        dataHCount = 1 # 水平方向索引
        for contract_key in firstHList:
            dataHCount = dataHCount + 1
            # 数据索引
            dataHIndex = HIndexs[ dataHCount ] + str(dataVIndex)
            contractDict = data[ 1 ]
            # 交易对收益/交易对/天
            try:
                ear = contractDict[ contract_key ]
                worksheek[ dataHIndex ] = ear
            except:
                print(dataKey + "没有" + contract_key)
    

    # 保存文件
    workbook.save(filePath)

def calculateVolumeOfTransaction(filePath):
    """计算交易笔数（按小时统计）
    :param str filePath: 文件路径 (required)
    :return: dict volumes
    """
    volumes = {}
    with open(filePath, 'r') as file:  

        text = file.read()  # 读取整个文件  

    # 匹配JSON字符串  
    pattern = r'({.*?})'
    match = re.findall(pattern, text)

    for m in match:
       
        order_dict = json.loads(m)
        timestamp = order_dict[ "create_time" ]
        # 使用datetime.fromtimestamp()函数将时间戳转换为datetime对象  
        dt_object = datetime.datetime.fromtimestamp(timestamp)  
        create_time = dt_object.strftime("%H")

        if create_time not in volumes:
            # 创建列表并加入索引中
            volume = 0
        else:
            volume = volumes[ create_time ]
        volume = volume + 1
        volumes[ create_time ] = volume

    # print(volumes)
    # 使用sorted函数和lambda表达式按照值降序排序  
    sorted_dict = dict(sorted(volumes.items(), key=lambda item: item[1], reverse=True))  
  
    # 打印排序后的字典  
    print(sorted_dict)  


def calculateWGEarnings(records, quanto_multiplier):
    """计算网格收益（递归）
    :param list records: 订单列表 (required)
    :param str quanto_multiplier: 合约每张所含币数量 (required)
    :return: (float earning, list remainOrder)
    """
    earnings = 0.0
    order_count = len(records)
    index = 0

    if len(records) < 2 :
        return (earnings, records)
    while index < (order_count-1):

        first_order = records[ index ]
        second_order = records[ index + 1 ]
        if first_order["size"] * second_order["size"] < 0:
            # 交易币量（单个订单）
            size = first_order["size"]
            coin_size = abs(size) * quanto_multiplier
            # 空单价格
            sell_price = max(float(first_order["price"]), float(second_order["price"]))
            # 多单价格
            buy_price = min(float(first_order["price"]), float(second_order["price"]))
            # 交易费率
            fee = float(first_order["tkfr"])
            # 交易手续费（多单+空单）
            charge = sell_price * coin_size * fee + buy_price * coin_size * fee
            # 单网格收益
            ear = (sell_price - buy_price) * coin_size - charge

            # 累计网格收益
            earnings = earnings + ear
            # 移除两个已经配对的订单
            records.pop(index+1)
            records.pop(index)
            break
        index = index + 1
    if index >= (order_count - 1):
        return (earnings, records)
    else:
        result_tup = calculateWGEarnings(records, quanto_multiplier)
        earnings = earnings + result_tup[0]
        return (earnings, records)

def get_quanto_multiplier(contract):
    """获取合约每张所含币数量
    :param str contract: 交易对 (required)
    :return: quanto_multiplier
    :rtype: str
    """

    with open("contractList.json", 'r') as file:  
        text = file.read()  # 读取整个文件 
    list = json.loads(text) # [contractDict,contractDict...]
     # 交易对索引
    contract_indexes = {}
    for dict in list:
        contract_name = dict["name"]
        contract_indexes[contract_name] = dict # 添加到索引字典里
    # 获取目标交易对 字典
    result_dict = contract_indexes[contract]
    # 获取目标交易对 币/张
    quanto_multiplier = result_dict["quanto_multiplier"]

    return quanto_multiplier

def readRecords(filePath):
    """读取文件提取字典，并根据交易对分类
    :param str filePath: 文件路径 (required)
    :return: contract_indexes
    :rtype: dict
    """

    with open(filePath, 'r') as file:  
        text = file.read()  # 读取整个文件  

    # 匹配JSON字符串  
    pattern = r'({.*?})'
    match = re.findall(pattern, text)  

    # 交易对索引（eg.{ "ETH_USDT" : [{"price":2221,"size":-1,...},...]}）
    contract_indexes = {}
    # 提取订单并根据交易对并分类（将匹配到的字符串解析为字典）
    if match:  
        # 遍历订单JSON列表
        for m in match:
            # 将订单JSON字符串 转化为 字典
            data = json.loads(m)
            contract_name = data["contract"]
            # 如果索引中不存在该交易对
            if contract_name not in contract_indexes:
                # 创建列表并加入索引中
                order_list = []
                contract_indexes[contract_name] = order_list
            # 从索引中获取contract_name交易对订单列表
            order_list = contract_indexes[contract_name]
            # 将订单字典加入contract_name交易对订单列表中
            order_list.append(data)
            # ⭕️ 打印信息，可删除*********
            # print("---%s count: %d" % (contract_name, len(order_list)))
            # l = []
            # for o in order_list:
            #     p = o["price"]
            #     size = o["size"]
            #     if size > 0 :
            #         l.append(p)
            #     else:
            #         l.append("-" + p)
            # dd = json.dumps(l)
            # print("===== %s =====\n %s" % (contract_name, dd))
            # ⭕️ *************************
    return contract_indexes

def getFirstEarningForWG(records, quanto_multiplier):
    """根据交易记录计算首个网格收益
    :param list records
    :param float quanto_multiplier 每张合约所含的币量
    :return: (earning, last_records) 返回网格收益 和 剩余记录
    :rtype: (float, list) 
    """
    temp_records = records
    order_count = len(temp_records)
    # 订单数小于2，不存在网格
    if order_count < 2:
        return (0.0, temp_records)
    earning = 0.0
    index = 0
    
    while index < (order_count-1):
        first_order = temp_records[ index ]
        second_order = temp_records[ index + 1 ]
        if first_order["size"] * second_order["size"] < 0:
            # 交易币量（单个订单）
            size = first_order["size"]
            coin_size = abs(size) * quanto_multiplier
            # 空单价格
            sell_price = max(float(first_order["price"]), float(second_order["price"]))
            # 多单价格
            buy_price = min(float(first_order["price"]), float(second_order["price"]))
            # 交易费率
            fee = float(first_order["tkfr"])
            # 交易手续费（多单+空单）
            charge = sell_price * coin_size * fee + buy_price * coin_size * fee
            # 单网格收益
            earning = (sell_price - buy_price) * coin_size - charge


            # 移除两个已经配对的订单
            temp_records.pop(index+1)
            temp_records.pop(index)
            break
        index = index + 1
    return (earning, temp_records)


def getEarningsForWG(records, quanto_multiplier):
    """根据交易记录计算网格收益
    :param list records
    :param float quanto_multiplier 每张合约所含币量
    :return: (earnings, last_records) 返回网格收益 和 未成对记录
    :rtype: (float, list) 
    """
    # 网格收益
    wg_earning = 0.0
    # 下一个计算网格的订单记录列表
    next_records = copy.deepcopy(records)
    # 上一次列表循环的订单数
    last_count = len(next_records)
    while 1:
        result = getFirstEarningForWG(next_records, quanto_multiplier)
        earning = result[0]
        # print("---%f:%s" % (earning, result[1]))
        result_records = result[1]

        wg_earning = wg_earning + earning
        # 没有剩余订单，跳出循环
        if len(result_records) == 0:
            next_records = result_records
            break
        # 没有匹配到网格，跳出循环
        if len(result_records) == last_count:
            next_records = result_records
            break
        next_records = result_records
        last_count = len(next_records)

    
    return (wg_earning, next_records)

 
def getEarningsForRecordByIteration(filePath):
    """根据交易记录文件计算收益 (迭代函数)
    :param str filePath: 文件路径 (required)
    """
    print("📅📅📅 %s" % filePath)

    # 计算结果（ eg. { contract_name : 9.88 } ）
    result_dict = {}

    contract_indexes = readRecords(filePath)
    # 所有交易对网格累计收益
    wg_earning_total = 0.0
    # 遍历索引中的所有交易对
    for contract_key in contract_indexes:
        # 获取contract_key交易对的订单列表
        list = contract_indexes[contract_key]
        # 每张合约所含有的币数
        quanto_multiplier = float(get_quanto_multiplier(contract_key))
        result = getEarningsForWG(list, quanto_multiplier)
        result_dict[contract_key] = result[0]
        print("✅✅✅ %s : %f" % (contract_key ,result[0]))
        # print("⭕️ 剩余 : %s" % result[1])
        wg_earning_total = wg_earning_total + result[0]

    print("🌈✅✅✅✅✅ 网格总收益：%f" % wg_earning_total)
    return (wg_earning_total, result_dict)

def getEarningsForRecordByRecursion(filePath):
    """根据交易记录文件计算收益 (递归函数)
    :param str filePath: 文件路径 (required)
    """
    contract_indexes = readRecords(filePath)
    # 所有交易对网格累计收益
    wg_earning_total = 0.0
    # 遍历索引中的所有交易对
    for contract_key in contract_indexes:
        # 获取contract_key交易对的订单列表
        list = contract_indexes[contract_key]
        # 每张合约所含有的币数
        quanto_multiplier = float(get_quanto_multiplier(contract_key))
        result = calculateWGEarnings(list, quanto_multiplier)
        print("✅✅✅ %s : %f" % (contract_key ,result[0]))
        # print("⭕️ 剩余 : %s" % result[1])
        wg_earning_total = wg_earning_total + result[0]

    print("🌈✅✅✅✅✅ 网格总收益：%f" % wg_earning_total)


def getEarningsFromWangge(price, count, rate, size, takeFee, quanto_multiplier, contract):
    """获取网格收益
    :param float price: 交易价格 (required)
    :param int count: 交易对 (required)
    :param float rate: 涨跌幅度 (required)
    :param int size: 交易量 (required)
    :param float takeFee: 吃单费率 (required)
    :param float quanto_multiplier: 币/张 (required)
    :param str contract: 交易对 (required)

    :return: earnings 收益
    """
    earnings = 0.0
    earnings = price * count * (rate - takeFee * 2) * size * quanto_multiplier
    print("✅ %s: %g" % (contract, earnings))

    return earnings

# 获取到达指定价格需要的合约张数
def getWanggeSizeForFinallyPrice(finallyPrice, singleRate, initPrice, singleSize):

    # 价格差距
    differenceInPrice = finallyPrice - initPrice
    # 涨跌比例
    totalRate = differenceInPrice / initPrice
    # 交易次数
    tradingCount = int(totalRate / singleRate)
    totalSize = tradingCount * singleSize
    
    return totalSize

def getPercentDepth(last_price):
    """计算百分比深度
    :param str last_price: 最后成交价 (required)

    :return: dept eg. '0.001'
    """
    price = float(last_price)
    # 深度
    dept = 1.0
    if 100 <= price < 1000:
        return str(int(dept))

    if price > 1000:
        temp_price = price
        temp_dept = dept
        #        指数
        n = 1
        while temp_price > 1000:
            temp_dept = dept * (10**n)
            temp_price = price / temp_dept
            n = n + 1
        return str(int(temp_dept))

    if price < 100:
        temp_dept = dept
        #        指数
        n = 1
        temp_price = price
        while temp_price < 100:
            temp_dept = dept * (0.1**n)
            temp_price = price / temp_dept
            n = n + 1
        return str(f"{temp_dept:.{n-1}f}")
    
def getScaleOfPendingOrders(settle, apiKey, apiSecret):
    """获取 settle 市场所有交易对多空挂单比
    :param str settle: 市场 eg.usdt (required)

    :return: scale eg. { "BTC_USDT" : 3.1 , "POGAI_USDT" : 0.4 }
    """
    # 获取当前工作目录
    current_directory = os.getcwd()
    # 构建文件路径
    file_path = os.path.join(current_directory, "contractList.json")
    # 打开文件
    with open(file_path, "r") as file:
        data = json.load(file)

    # API管理工具
    hostUsed = "https://api.gateio.ws/api/v4"
    config = Configuration(key=apiKey, secret=apiSecret, host=hostUsed)
    futures_api = FuturesApi(ApiClient(config))

    result_dict = {}
    for item in data:
        contract_name = item["name"]
        last_price = item["last_price"]
        # 计算合适的深度
        dept_interval = getPercentDepth(last_price)
        total_size = totalSize(settle, contract_name, dept_interval, 10, futures_api)
        size_ask = total_size["ask"]
        size_bid = total_size["bid"]
        #        多空比
        scale = 0.0
        if size_ask > 0 :
            scale = size_bid / size_ask
        #        添加到索引字典里
        result_dict[contract_name] = scale

    # 获取当前工作目录
    current_directory = os.getcwd()
    # 构建文件路径
    file_path = os.path.join(current_directory, "scaleOfPendingOrders.json")
    # 打开文件
    with open(file_path, "w") as file:
        # 将数组写入文件
        json.dump(result_dict, file)

    return result_dict

def totalSize(settle, contract, interval, limit, futures_api, showBook=False):
    """累计挂单

    :param str settle: 市场 eg.usdt (required)
    :param str contract: 交易对 (required)
    :param str interval: 深度 (required)
    :param int limit: 获取条数 (required)
    :param FuturesApi futures_api: API对象 (required)
    :param bool showBook: 是否打印挂单明细


    :return: { "ask" : askTotalSize , "bid" : bidTotalSize }
    """

    list_fututes = futures_api.list_futures_order_book(
        settle, contract, limit=limit, interval=interval, async_req=True
    )

    asks = list_fututes.get().asks
    bids = list_fututes.get().bids

    totalSize_ask = 0
    totalSize_bid = 0
    for ask in asks:
        totalSize_ask += ask.s

    for bid in bids:
        totalSize_bid += bid.s

    print("%s 卖单挂单量： %s" % (contract, totalSize_ask))
    print("%s 买单挂单量： %s" % (contract, totalSize_bid))

    if showBook:
        print("%s 挂单详细: \n%s"% (contract, (list_fututes.get())))

    return {"ask": totalSize_ask, "bid": totalSize_bid}

def lastPrice(settle, contract, futures_api):
    """最后成交价

    :param str settle: Settle currency (required)
    :param str contract: 交易对 (required)
    :param FuturesApi futures_api: API对象 (required)

    :return: If the method is called asynchronously,
                returns the request thread.
    """

    tickers = futures_api.list_futures_tickers(settle, contract=contract)
    assert len(tickers) == 1
    last_price = tickers[0].last
    print("%s 最后成交价: %s" % (contract, last_price))
    print("list price of contract %s: \n%s" % (contract, tickers))
    return last_price

def updateContractList(settle, futures_api):
    """更新 settle 合约市场交易对列表"""
    
    futures_contracts = futures_api.list_futures_contracts(settle)
    futures_dicts = [contract.to_dict() for contract in futures_contracts]

    # 获取当前工作目录
    current_directory = os.getcwd()
    # 构建文件路径
    file_path = os.path.join(current_directory, "contractList.json")
    # 打开文件
    with open(file_path, "w") as file:
        # 将数组写入文件
        json.dump(futures_dicts, file)

def updateStatisticalFile():
    """更新统计文件"""

    month = "2024-04"
    day = 1
    lastDay = 31
    contract_list = [
        "PEPE2_USDT",
        "ETH_USDT",
        "WLD_USDT",
        "LINK_USDT",
        "DOGE_USDT",
        "ORDI_USDT",
        "SHIB_USDT",
        "XRP_USDT",
        "BONK_USDT",
        "BTC_USDT",
        "AVAX_USDT",
        "ARB_USDT",
        "BNB_USDT",
        "SOL_USDT",
        "SUI_USDT",
        "APT_USDT",
        "FIL_USDT",
        "PEPE_USDT"
    ]

    dataDicts = {}
    # 计算收益/天
    recordRootPath = "/Users/chenrongjian/Desktop/网格记录/"
    while day <= lastDay :
        dayStr = str(day).zfill(2)
        dateStr = (month + "-" + dayStr)
    
        filePath = recordRootPath + "YYGateStrategy_" + dateStr + ".log"

        # 判断文件是否存在  
        if os.path.exists(filePath):  
            print(f"{filePath} 文件存在。")  
            day_data = getEarningsForRecordByIteration(filePath)
            dataDicts[ dateStr ] = day_data
        else:  
            print(f"{filePath} 文件不存在。")

        day = day + 1
    # 计算收益/月
    filePath = recordRootPath + "YYGateStrategy.log"
    month_data = getEarningsForRecordByIteration(filePath)
    dataDicts[ month ] = month_data

    writeToExel("/Users/chenrongjian/Desktop/网格记录/统计.xlsx", contract_list, dataDicts)



def main():

    apiKey = 'a8c715a17b856dda7d3440059ca05e42'
    apiSecret = '0a89a221048767617351b3803c9a15bf0892c28f8892e1445a7ef2470427cfac'
    settle = 'usdt'
     # API管理工具
    hostUsed = "https://api.gateio.ws/api/v4"
    config = Configuration(key=apiKey, secret=apiSecret, host=hostUsed)
    futures_api = FuturesApi(ApiClient(config))
    
    # updateContractList(settle, futures_api)
    # return

    # getScaleOfPendingOrders(settle, apiKey, apiSecret)
    # return
    sell_buy_comparison(apiKey, apiSecret)
    return
    # 更新统计数据
    updateStatisticalFile()
    return
    # 计算交易量
    calculateVolumeOfTransaction("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2024-02-20.log")
    return
    # calculateVolumeOfTransaction("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2024-02-18.log")
    # return
    # YYGateStrategy
    # calculateVolumeOfTransaction("/Users/chenrongjian/Desktop/网格记录/23-12/YYGateStrategy_2023-12.log")

    # return
    # sys.setrecursionlimit(10000)
    # getEarningsForRecordByRecursion("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy.log")

    # getEarningsForRecordByIteration("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2023-12.log")
    # getEarningsForRecordByIteration("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2024-02-14.log")
    # getEarningsForRecordByIteration("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2024-02-15.log")
    getEarningsForRecordByIteration("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy_2024-02-21.log")

    # getEarningsForRecordByIteration("/Users/chenrongjian/Desktop/网格记录/YYGateStrategy.log")

    return

if __name__ == '__main__':
    main()